home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1998 November: Tool Chest / Dev.CD Nov 98 TC.toast / Sample Code / Networking / OTDumpInternetStatus1.0b1 / OTDumpInternetStatus.c next >
Encoding:
C/C++ Source or Header  |  1998-02-25  |  10.9 KB  |  354 lines  |  [TEXT/CWIE]

  1. /*
  2.     File:        OTDumpInternetStatus.c
  3.  
  4.     Contains:    Dumps the status of the TCP/IP stack to stdout.
  5.  
  6.     Written by:    Quinn "The Eskimo!"
  7.  
  8.     Copyright:    © 1998 by Apple Computer, Inc., all rights reserved.
  9.  
  10.     Change History (most recent first):
  11.  
  12.     You may incorporate this sample code into your applications without
  13.     restriction, though the sample code has been provided "AS IS" and the
  14.     responsibility for its operation is 100% yours.  However, what you are
  15.     not permitted to do is to redistribute the source as "DSC Sample Code"
  16.     after having made changes. If you're going to re-distribute the source,
  17.     we require that you make it clear in the source that the code was
  18.     descended from Apple Sample Code, but that you've made changes.
  19. */
  20.  
  21. #define qDebug 1
  22.  
  23. /////////////////////////////////////////////////////////////////////
  24. // Pick up standard C stuff.
  25.  
  26. #include <stdio.h>
  27. #include <string.h>
  28.  
  29. /////////////////////////////////////////////////////////////////////
  30. // Pick up standard OT APIs.
  31.  
  32. #include <OpenTptInternet.h>
  33.  
  34. /////////////////////////////////////////////////////////////////////
  35. // Pick up low-level OT APIs.
  36.  
  37. #include <OpenTptClient.h>
  38. #include <OTDebug.h>
  39.  
  40. /////////////////////////////////////////////////////////////////////
  41. // Pick up the symbolic name of the various OT modules.
  42.  
  43. #include <modnames.h>
  44.  
  45. /////////////////////////////////////////////////////////////////////
  46. // OTDebugStr is not defined in any OT header files, but it is
  47. // exported by the libraries, so we define the prototype here.
  48.  
  49. extern pascal void OTDebugStr(const char* str);
  50.  
  51. /////////////////////////////////////////////////////////////////////
  52. // The following equates are actually exported by <miioccom.h>, but
  53. // they commented out for some reason )-:
  54.  
  55. // #include <miioccom.h>
  56.  
  57. #define MIOC_ND            'c'        /* ioctl's for Mentat's nd device */
  58.  
  59. // The following equates define the two "Name Dispatch" ioctls
  60. // for setting and getting OT internal parameters.
  61.  
  62. #define ND_GET            MIOC_CMD(MIOC_ND, 0)    /* Get a value */
  63. #define ND_SET            MIOC_CMD(MIOC_ND, 1)    /* Set a value */
  64.  
  65. /////////////////////////////////////////////////////////////////////
  66. // The name of the Name Dispatch variables we display.
  67.  
  68. #define ARP_ND_CACHE_REPORT     "arp_cache_report"
  69.     // ARP cache report
  70.  
  71. #define IP_ND_INTERFACE_STATUS    "ip_ipif_status"
  72.     // List of active logical interfaces
  73.  
  74. #define IP_ND_LINK_STATUS         "ip_ill_status"
  75.     // List of active physical interfaces
  76.     
  77. #define IP_ND_ROUTE_STATUS        "ip_ire_hash"
  78.     // List of IP Routing Entries (IREs), grouped
  79.     // by table in the order searched.
  80.  
  81. #define IP_ND_ROUTE_STATUS2        "ip_ire_status"
  82.     // List of IP Routing Entries (IREs) without
  83.     // grouping.
  84.     
  85. #define TCP_ND_STATUS            "tcp_status"
  86.     // TCP status
  87.     
  88. #define UDP_ND_STATUS            "udp_status"
  89.     // UDP status
  90.  
  91. /////////////////////////////////////////////////////////////////////
  92.  
  93. static OSStatus CreateStatusStream(StreamRef *result)
  94.     // Create a raw stream to which we can send the various
  95.     // status reports.  We do this by opening the IP driver,
  96.     // and pushing the other modules directly on top of it.
  97.     // This arrangement is just for convenience.  We could
  98.     // just have easily opened the null driver and pushed
  99.     // the module of interest on top.
  100. {
  101.     OSStatus err;
  102.     OSStatus junk;
  103.     StreamRef strm;
  104.     
  105.     // Open up a raw stream to the IP device.
  106.     
  107.     strm = OTStreamOpen(MI_IP_NAME, 0, &err);
  108.     if (err == noErr) {
  109.         // To make this simpler we're going to use sync/blocking mode.
  110.         
  111.         OTStreamSetBlocking(strm);
  112.         OTStreamSetSynchronous(strm);
  113.     }
  114.  
  115.     // Push the various modules of interest on top of the stream.
  116.     
  117.     if (err == noErr) {
  118.         err = OTStreamIoctl(strm, I_PUSH, MI_ARPM_NAME);
  119.     }
  120.     if (err == noErr) {
  121.         err = OTStreamIoctl(strm, I_PUSH, MI_TCPM_NAME);
  122.     }
  123.     if (err == noErr) {
  124.         err = OTStreamIoctl(strm, I_PUSH, MI_UDPM_NAME);
  125.     }
  126.     
  127.     // Clean up and setup result to either be valid or nil.
  128.     
  129.     if (err == noErr) {
  130.         *result = strm;
  131.     } else {
  132.         if (strm != kOTInvalidStreamRef) {
  133.             junk = OTStreamClose(strm);
  134.             OTAssert("CreateStatusStream: OTStreamClose failed", junk == noErr);
  135.         }
  136.         *result = kOTInvalidStreamRef;
  137.     }
  138.     return err;
  139. }
  140.  
  141. static void DumpNameDispatchReport(StreamRef strm, char *ndName, char *userVisibleName)
  142.     // Dumps a Name Dispatch (ND) report to standard out.  strm
  143.     // is a raw stream that contains the module from which the
  144.     // report is to be extracted.  ndName is the Name Dispatch
  145.     // name of the report.  userVisibleName is the name of the report
  146.     // in user terminology (only used to make the printout sensible).
  147.     //
  148.     // The general  principle is as follows.  We send an Name Dispatch ioctl
  149.     // down strm.  The relevant module catches the ioctl, creates the
  150.     // report (as text, with null characters as the line terminator)
  151.     // and sends it back to us.  The stream head copies the data back
  152.     // into our ioctl buffer.
  153.     //
  154.     // The only tricky thing is to judge the size of the buffer to
  155.     // allocate.  We do this in two passes.  In the first pass,
  156.     // we create a minimum sized buffer and use it for the ioctl.
  157.     // The ioctl result comes back as the size of the buffer we
  158.     // should have allocated.  We then reallocate the buffer
  159.     // and issue the ioctl again.  Obviously the size of the report
  160.     // could change between successive ioctls, so we have to
  161.     // loop until it works correctly.
  162. {
  163.     OSStatus err;
  164.     struct strioctl ndIoctl;
  165.     SInt32 i;
  166.     char *dataBuffer;
  167.     SInt32 dataBufferSize;
  168.     SInt32 minimumDataBufferSize;
  169.     SInt32 ioctlResult;
  170.     Boolean done;
  171.     
  172.     printf("Dumping %s (%s)\n\n", userVisibleName, ndName);
  173.     
  174.     // Allocate a minimum sized buffer for the first ioctl call.
  175.     // It's the length of the string, plus space for the null terminator,
  176.     // plus space for an extra null.
  177.     
  178.     dataBuffer = nil;
  179.     dataBufferSize = OTStrLength(ndName) + 1 + 1;
  180.     minimumDataBufferSize = dataBufferSize;
  181.     
  182.     done = false;
  183.     
  184.     do {
  185.         OTAssert("DumpNameDispatchReport: dataBuffer should have been disposed in the looping case", dataBuffer == nil);
  186.     
  187.         // Allocate the memory according to our current guess as to dataBufferSize.
  188.  
  189.         err = noErr;
  190.         dataBuffer = OTAllocMem( dataBufferSize );
  191.         if (dataBuffer == nil) {
  192.             err = kENOMEMErr;
  193.         }
  194.         
  195.         if (err == noErr) {
  196.  
  197.             // Copy the name of the ND variable we're trying
  198.             // to get into our buffer.
  199.  
  200.             OTStrCopy(dataBuffer, ndName);
  201.             
  202.             // Now put a null after the name in the data buffer.
  203.             // This is because ND requests must be made up
  204.             // of two strings, right after one another in the
  205.             // buffer.
  206.             
  207.             dataBuffer[ OTStrLength(ndName) + 1 ] = 0;
  208.         
  209.             // The ND_GET ioctl returns a value and sets ic_len.  A negative
  210.             // value is an error and you can give up now (-:  The rule for
  211.             // positive values is a bit weirder.  ic_len is always set
  212.             // to the amount of data that is actually returned.  If the
  213.             // data available exceeds the available buffer space (as
  214.             // defined by the ic_len on input), the ioctl returns
  215.             // a positive number that is the amount of buffer space
  216.             // needed.  So we first call it with a minimal buffer
  217.             // then give it the buffer space it requires.  Obviously
  218.             // there's a concurrency race here; we loop until our
  219.             // buffer is big enough.
  220.  
  221.             // First get the size of data buffer we need to allocate.
  222.             
  223.             ndIoctl.ic_cmd = ND_GET;
  224.             ndIoctl.ic_timout = 0;
  225.             ndIoctl.ic_len = dataBufferSize;
  226.             ndIoctl.ic_dp = dataBuffer;
  227.  
  228.             ioctlResult = OTStreamIoctl(strm, I_STR, &ndIoctl);
  229.             
  230.             // printf("••• dataBufferSize = %ld, ic_len = %ld, ioctlResult = %ld\n", dataBufferSize, ndIoctl.ic_len, ioctlResult);
  231.             
  232.             if (ioctlResult < 0) {
  233.                 err = ioctlResult;
  234.             } else {
  235.                 if (ioctlResult <= dataBufferSize) {
  236.                 
  237.                     // The report fit into dataBuffer, so let's
  238.                     // just print it out and we're done.  Remember that
  239.                     // the report uses nulls as line terminators, so we 
  240.                     // have to print it character by character )-:
  241.                     
  242.                     err = noErr;
  243.                     for (i = 0; i < ndIoctl.ic_len; i++) {
  244.                         if (dataBuffer[i] == 0) {
  245.                             putchar('\n');
  246.                         } else {
  247.                             putchar(dataBuffer[i]);
  248.                         }
  249.                     }
  250.                     done = true;
  251.                 } else {
  252.                 
  253.                     // The allocated data buffer is the wrong size,
  254.                     // so we deallocate and loop.
  255.                     
  256.                     OTAssert("DumpNameDispatchReport: Should have a data buffer here", dataBuffer != nil);
  257.                     OTFreeMem(dataBuffer);
  258.                     dataBuffer = nil;
  259.  
  260.                     // In this case, the ioctl has returned the size that
  261.                     // the buffer /should have been/ to get all the info.  We
  262.                     // set dataBufferSize to that value and loop.
  263.                     //
  264.                     // The buffer that we allocate should be able to hold
  265.                     // the request (ie the string (with null terminator)
  266.                     // and the second null).  If the ioctlResult comes
  267.                     // back too small, we're going to die when copying
  268.                     // the string into the new buffer.  However, the ioctlResult
  269.                     // should be bigger than the buffer, because otherwise it
  270.                     // wouldn't have failed.  So we just assert that
  271.                     // ioctlResult >= minimumDataBufferSize, just to be sure.
  272.  
  273.                     OTAssert("DumpNameDispatchReport: ioctl failed but it should have succeeded", ioctlResult >= minimumDataBufferSize);
  274.  
  275.                     dataBufferSize = ioctlResult;
  276.                 }
  277.             }
  278.         }
  279.     } while (err == noErr & ! done );
  280.     
  281.     // Clean up.
  282.     if (dataBuffer != nil) {
  283.         OTFreeMem(dataBuffer);
  284.     }
  285.     
  286.     if (err == noErr) {
  287.         printf("Success!\n");
  288.     } else {
  289.         printf("Failed with error %ld.\n", err);
  290.     }
  291.     printf("\n\n");
  292. }
  293.  
  294. /////////////////////////////////////////////////////////////////////
  295.  
  296. void main(void)
  297. {
  298.     OSStatus err;
  299.     OSStatus junk;
  300.     StreamRef strm;
  301.     InetInterfaceInfo junkInfo;
  302.     
  303.     printf("Hello Cruel World!\n");
  304.     printf("OTDumpInternetStatus -- Dumps the state of the OT TCP/IP stack to stdout\n\n");
  305.     
  306.     err = InitOpenTransport();
  307.     
  308.     if (err == noErr) {
  309.     
  310.         err = OTInetGetInterfaceInfo(&junkInfo, kDefaultInetInterface);
  311.         if (err != noErr) {
  312.             printf("This report is not meaningful unless the TCP/IP stack is loaded.\n");
  313.             printf("You can still get the report, it just low on useful content.\n");
  314.             printf("\n");
  315.             err = noErr;
  316.         }
  317.         
  318.         if (err == noErr) {
  319.         
  320.             // Create the raw stream from which we're going to extract
  321.             // report information.  This stream contains all the TCP/IP
  322.             // modules ganged together in one convenient package.
  323.         
  324.             err = CreateStatusStream(&strm);
  325.             if (err == noErr) {
  326.             
  327.                 // Get and dump each report, one at a time.
  328.         
  329.                 DumpNameDispatchReport(strm, ARP_ND_CACHE_REPORT, "ARP Cache");
  330.                 DumpNameDispatchReport(strm, IP_ND_INTERFACE_STATUS, "IP Logical Interfaces");
  331.                 DumpNameDispatchReport(strm, IP_ND_LINK_STATUS, "IP Physical Interfaces");
  332.                 DumpNameDispatchReport(strm, IP_ND_ROUTE_STATUS, "IP Routing Table");
  333.                 DumpNameDispatchReport(strm, IP_ND_ROUTE_STATUS2, "IP Routing Table (without grouping)");
  334.                 DumpNameDispatchReport(strm, TCP_ND_STATUS, "TCP Status");
  335.                 DumpNameDispatchReport(strm, UDP_ND_STATUS, "UDP Status");
  336.                 
  337.                 // Clean up.
  338.                 
  339.                 junk = OTStreamClose(strm);
  340.                 OTAssert("main: OTStreamClose failed", junk == noErr);
  341.             }
  342.         }
  343.         
  344.         CloseOpenTransport();
  345.     }
  346.     
  347.     if (err == noErr) {
  348.         printf("Success.\n");
  349.     } else {
  350.         printf("Failed with error %d.\n", err);
  351.     }
  352.     printf("Done.  Press command-Q to Quit.\n");
  353. }
  354.